package validator

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
	"reflect"
	"strings"
)

// Validator 全局验证器实例
var Validator *validator.Validate

// 初始化验证器
func init() {
	Validator = validator.New()

	// 注册结构体字段名称函数，使用json标签作为字段名
	Validator.RegisterTagNameFunc(func(field reflect.StructField) string {
		name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
		if name == "-" {
			return ""
		}
		return name
	})
}

// ValidateStruct 验证结构体
func ValidateStruct(obj interface{}) error {
	return Validator.Struct(obj)
}

// BindAndValidate 绑定并验证请求参数
func BindAndValidate(c *gin.Context, obj interface{}) error {
	// 根据Content-Type绑定参数
	var err error
	contentType := c.GetHeader("Content-Type")

	if strings.Contains(contentType, "application/json") {
		err = c.ShouldBindJSON(obj)
	} else if strings.Contains(contentType, "multipart/form-data") {
		err = c.ShouldBindWith(obj, binding.Form)
	} else if strings.Contains(contentType, "application/x-www-form-urlencoded") {
		err = c.ShouldBindWith(obj, binding.Form)
	} else {
		// 默认尝试JSON绑定
		err = c.ShouldBindJSON(obj)
	}

	// 处理绑定错误
	if err != nil {
		return err
	}

	// 验证结构体
	return ValidateStruct(obj)
}

// FormatValidationErrors 格式化验证错误
func FormatValidationErrors(err error) string {
	if validationErrors, ok := err.(validator.ValidationErrors); ok {
		errMsgs := make([]string, 0, len(validationErrors))

		for _, e := range validationErrors {
			field := e.Field()
			tag := e.Tag()

			switch tag {
			case "required":
				errMsgs = append(errMsgs, fmt.Sprintf("%s不能为空", field))
			case "min":
				errMsgs = append(errMsgs, fmt.Sprintf("%s长度不能小于%s", field, e.Param()))
			case "max":
				errMsgs = append(errMsgs, fmt.Sprintf("%s长度不能大于%s", field, e.Param()))
			case "email":
				errMsgs = append(errMsgs, fmt.Sprintf("%s必须是有效的邮箱地址", field))
			case "url":
				errMsgs = append(errMsgs, fmt.Sprintf("%s必须是有效的URL", field))
			default:
				errMsgs = append(errMsgs, fmt.Sprintf("%s验证失败：%s", field, tag))
			}
		}

		if len(errMsgs) > 0 {
			return strings.Join(errMsgs, "；")
		}
	}

	return err.Error()
}
